﻿using AutoMapper;
using Benefactor.Jobs;
using Benefactor.Models.DbModels;
using Benefactor.Models.DtoModels;
using Benefactor.Services.Repositories;
using Bigway.Common.Jobs;
using Bigway.Common.Models.DtoModels;
using Bigway.Common.Services.Repositories;
using Bigway.Common.Validations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;

namespace Benefactor.Controllers;
using static Errors;

[ApiController]
[Route("api/referer_commission_withdrawal")]
public class RefererCommissionWithdrawalController(
    IRefererCommissionWithdrawalRecordRepository refererCommissionWithdrawalRecordRepository,
    IRefererCommissionRepository refererCommissionRepository,
    ISoftwareSystemRepository softwareSystemRepository,
    IMapper mapper,
    JobRegistryProvider jobRegistryProvider,
    ILogger<RefererCommissionWithdrawalController> logger): ControllerBase
{
    [HttpPost]
    public async Task<AddRefererCommissionWithdrawalResponseDto> AddRefererCommissionWithdrawalAsync(
        [FromBody] AddRefererCommissionWithdrawalRequestDto request,
        [FromServices] IValidator<AddRefererCommissionWithdrawalRequestDto> validator,
        [FromQuery(Name = "api_key")] string apiKey,
        CancellationToken token = default
    )
    {
        var systemId = request.SystemId;
        var refererAccount = request.RefererAccount;
        logger.LogInformation("Adding withdrawal record for referer account {RefererAccount}", refererAccount);
        var result = await validator
            .ValidateRequestAsync<AddRefererCommissionWithdrawalRequestDto, AddRefererCommissionWithdrawalResponseDto>(
            apiKey, request, token);

        if (result != null)
            return result;

        var system = await softwareSystemRepository.GetAsync(systemId, token);
        if (system is null)
            return SystemNotFound<AddRefererCommissionWithdrawalResponseDto>(systemId);

        var refererCommissions = await refererCommissionRepository.GetAllAsync(
            query =>
                query.Where(x =>
                    x.SystemId == systemId &&
                    x.RefererAccount == refererAccount
                )
                .Includes(x => x.RefererCommissionWithdrawalRecords)
                .Includes(x => x.RefererCommissionRecords),
            token);

        if (refererCommissions.Count == 0)
            return RefererAccountNotFound<AddRefererCommissionWithdrawalResponseDto>(refererAccount);
        
        var refererCommission = refererCommissions[0];
        if (refererCommission.IsDeleted)
            return RefererAccountDeleted<AddRefererCommissionWithdrawalResponseDto>(refererAccount);

        if (refererCommission.TotalRefererFee < request.Amount)
            return RefererFeeBalanceNotEnough<AddRefererCommissionWithdrawalResponseDto>(refererAccount);

        var job = jobRegistryProvider.JobRegisty.GetJobInfo<CalculateAndGenerateRefererFeeJob>(systemId);
        job?.Job.Wait();

        var refererCommissionWithdrawalRecord = new RefererCommissionWithdrawalRecord
        {
            SystemId = systemId,
            RefererCommissionId = refererCommission.Id,
            Amount = request.Amount,
            WithdrawTime = DateTime.UtcNow
        };

        await refererCommissionWithdrawalRecordRepository.AddAsync(refererCommissionWithdrawalRecord, token);

        refererCommission.RefererCommissionWithdrawalRecords!.Add(refererCommissionWithdrawalRecord);

        await refererCommissionRepository.UpdateAsync(refererCommission, token);

        return new AddRefererCommissionWithdrawalResponseDto
        {
            Data = mapper.Map<RefererCommissionWithdrawalRecordInfo>(refererCommissionWithdrawalRecord)
        };
    }
}